package com.framework.common.mongodb.impl;

import com.framework.common.mongodb.builder.QueryBuilder;
import com.framework.common.mongodb.em.QueryModel;
import com.framework.common.mongodb.entity.QueryEntity;
import com.mongodb.BasicDBObject;
import com.mongodb.client.result.DeleteResult;
import com.framework.common.mongodb.BaseMongoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;

public abstract class BaseMongoServiceImpl<T> implements BaseMongoService<T> {

	@Autowired(required=false)
    @Qualifier("mongoTemplate")
    protected MongoTemplate mongoTemplate;
	
	private Class<T> paramClz;
	
	public Class<T> getParamClass(){
		if (paramClz == null) {
	        ParameterizedType type = (ParameterizedType)this.getClass().getGenericSuperclass();
	        paramClz = (Class)type.getActualTypeArguments()[0];//<T>
		}
        return paramClz;
    }
	
	@Override
	public T save(T bean) {
		return mongoTemplate.save(bean);
	}
	
	@Override
	public List<T> saveCollectio(List<T> list) {
		return (List<T>) mongoTemplate.insert(list, getParamClass());
	}

	@Override
	public void deleteById(T t) {
		mongoTemplate.remove(t);
	}

	@Override
	public void deleteByCondition(T t) {
		Query query = buildBaseQuery(t);
        mongoTemplate.remove(query, getEntityClass());
	}

	@Override
	public boolean deleteByCondition(Map<String, QueryEntity> queryMap, QueryModel model) {
		QueryBuilder builder = new QueryBuilder(queryMap, model);
		Criteria ca = builder.queryCriteria();
		Query query = new Query(ca);
		final DeleteResult result = mongoTemplate.remove(query, getEntityClass());
		return result.wasAcknowledged();
	}
	
	@Override
	public void deleteByCondition(Query query) {
        mongoTemplate.remove(query, getEntityClass());
	}

	@Override
	public void update(Query query, Update update) {
		mongoTemplate.updateMulti(query, update, this.getEntityClass());
	}

	@Override
	public void updateById(String id, T t) {
		Query query = new Query();
        query.addCriteria(Criteria.where("id").is(id));
        Update update = buildBaseUpdate(t);
        update(query, update);
	}

	@Override
	public List<T> find(Query query) {
		return mongoTemplate.find(query, this.getEntityClass());
	}

	@Override
	public List<T> findAll() {
		return mongoTemplate.findAll(getEntityClass());
	}

	@Override
	public List<T> listPage(Query query, Pageable pageable, Sort sort) {
		query.skip(pageable.getPageNumber());
		query.limit(pageable.getPageSize());
		if(null != sort){
			query.with(sort);
		}
		return this.find(query);
	}

	@Override
	public List<T> findByCondition(Map<String, QueryEntity> queryMap, QueryModel model) {
		QueryBuilder builder = new QueryBuilder(queryMap, model);
		Criteria ca = builder.queryCriteria();
		Query query = new Query(ca);
		return mongoTemplate.find(query, this.getEntityClass());
	}

	@Override
	public List<T> findByConditionAndPage(Map<String, QueryEntity> queryMap, QueryModel model, Pageable pageable, Sort sort) {
		QueryBuilder builder = new QueryBuilder(queryMap, model);
		Criteria ca = builder.queryCriteria();
		Query query = new Query(ca);
		return this.listPage(query, pageable, sort);
	}

	@Override
	public List<T> findByCondition(T t) {
		Query query = buildBaseQuery(t);
        return mongoTemplate.find(query, getEntityClass());
	}

	@Override
	public List<T> findByConditionAndSort(T t, Sort sort, String... resultFileds) {
		Query query = buildComplexQuery(t, sort, resultFileds);
        return mongoTemplate.find(query, getEntityClass());
	}
	
	@Override
	public List<T> findByConditionAndSortLimit(T t, Sort sort, int limit, String... resultFileds) {
		Query query = buildComplexQuery(t, sort, resultFileds).limit(limit);
        return mongoTemplate.find(query, getEntityClass());
	}
	
	@Override
	public T findOne(Query query) {
		return mongoTemplate.findOne(query, this.getEntityClass());
	}

	@Override
	public T get(String id) {
		return mongoTemplate.findById(id, this.getEntityClass());
	}

	@Override
	public T get(String id, String collectionName) {
		return mongoTemplate.findById(id, this.getEntityClass(), collectionName);
	}
	
	public Query buildComplexQuery(T t, Sort sort, String... resultFileds) {
		BasicDBObject fieldsObject = new BasicDBObject();
		
		if (resultFileds != null && resultFileds.length > 0) {
			for (String filedName : resultFileds) {
				fieldsObject.put(filedName, true);
			}
		}
		
		//设置查询条件
		BasicDBObject queryObject = new BasicDBObject();
		Field[] fields = t.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(t);
                if (value != null) {
                    org.springframework.data.mongodb.core.mapping.Field queryField = field.getAnnotation(org.springframework.data.mongodb.core.mapping.Field.class);
                    if (queryField != null) {
                    	queryObject.put(queryField.value(), value);
                    }
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
		
		//设置返回值
		Query query = new BasicQuery(queryObject.toJson(), fieldsObject.toJson());
		//sort
		query.with(sort);
		
        return query;
    }
	
	// 根据vo构建查询条件Query
    private Query buildBaseQuery(T t) {
        Query query = new Query();
        Field[] fields = t.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(t);
                if (value != null) {
                    org.springframework.data.mongodb.core.mapping.Field queryField = field.getAnnotation(org.springframework.data.mongodb.core.mapping.Field.class);
                    if (queryField != null) {
                        query.addCriteria(Criteria.where(queryField.value()).is(value));
                    }
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return query;
    }
    
    private Update buildBaseUpdate(T t) {
        Update update = new Update();
 
        Field[] fields = t.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(t);
                if (value != null) {
                	org.springframework.data.mongodb.core.mapping.Field queryField = field.getAnnotation(org.springframework.data.mongodb.core.mapping.Field.class);
                    if (queryField != null) {
                    	update.set(queryField.value(), value);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return update;
    }
 
    // 获取需要操作的实体类class
    @SuppressWarnings("unchecked")
    protected Class<T> getEntityClass() {
        return ((Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
    }

	@Override
	public MongoTemplate getMongoTemplate() {
		return mongoTemplate;
	}


	
}
